package tul.cot.util;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.math.stat.descriptive.DescriptiveStatistics;

import tul.cot.io.PlotPoint;
import tul.cot.util.StatisticsUtils.HpdRange;


/**
 * This class is used to aggregate all {@link PlotPoint}-s for a single label.
 * For each segment the following values are computed: median, hpd_min, hpd_max.
 */
public class MigAggregator {

//	public static final double DUMMY_VALUE = Double.MIN_VALUE;
	public static final double DUMMY_VALUE = 0;
	
	private static final int NO_SEGMENT = -1;
	
	private final int NUM_TREES;
	private final float HPD_CONF_LEVEL;
	
	private int currSeg = NO_SEGMENT;
	DescriptiveStatistics stats = new DescriptiveStatistics();
	
	private List<Double> medians = new ArrayList<Double>();
	private List<Double> hpdMin = new ArrayList<Double>();
	private List<Double> hpdMax = new ArrayList<Double>();
	
	public MigAggregator(int numTrees, float hpdConfLevel) {
		NUM_TREES = numTrees;
		HPD_CONF_LEVEL = hpdConfLevel;
	}
	
	/**
	 * @param points all {@link PlotPoint}-s of a single segment
	 */
	public void aggregate(Iterable<PlotPoint> points) {
		for (PlotPoint pp: points) {
			add(pp);
		}
		
		aggregateValues();
	}
	
	private void add(PlotPoint pp) {
		
		if (pp.getSegment() != currSeg ) {
			if (currSeg != NO_SEGMENT) {
				aggregateValues();
			}
			startNewSegment(pp.getSegment());
		}
		
		addValue(pp.getValue());
	}
	
	

	private void startNewSegment(int segNum) {
		stats.clear();
		currSeg = segNum;
	}

	private void addValue(double value) {
		if (value != DUMMY_VALUE) {
			stats.addValue(value);
		}
	}
	
	private void aggregateValues() {
		double[] sortedValues = stats.getSortedValues();
		
		final int numZeros = NUM_TREES - sortedValues.length;
		
		double median = StatisticsUtils.getMedian(sortedValues, numZeros, 0);
		HpdRange hpd = StatisticsUtils.getHpd(HPD_CONF_LEVEL, sortedValues, numZeros, 0);
		
		medians.add(median);
		hpdMin.add(hpd.min);
		hpdMax.add(hpd.max);
	}

	public List<Double> getMedianValues() {
		return medians;
	}

	public List<Double> getHpdMinValues() {
		return hpdMin;
	}
	
	public List<Double> getHpdMaxValues() {
		return hpdMax;
	}

}
